home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tk4.0 / tkGeometry.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-03-18  |  17.0 KB  |  583 lines

  1. /* 
  2.  * tkGeometry.c --
  3.  *
  4.  *    This file contains generic Tk code for geometry management
  5.  *    (stuff that's used by all geometry managers).
  6.  *
  7.  * Copyright (c) 1990-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  */
  13.  
  14. static char sccsid[] = "@(#) tkGeometry.c 1.28 95/03/17 16:01:55";
  15.  
  16. #include "tkPort.h"
  17. #include "tkInt.h"
  18.  
  19. /*
  20.  * Data structures of the following type are used by Tk_MaintainGeometry.
  21.  * For each slave managed by Tk_MaintainGeometry, there is one of these
  22.  * structures associated with its master.
  23.  */
  24.  
  25. typedef struct MaintainSlave {
  26.     Tk_Window slave;        /* The slave window being positioned. */
  27.     Tk_Window master;        /* The master that determines slave's
  28.                  * position; it must be a descendant of
  29.                  * slave's parent. */
  30.     int x, y;            /* Desired position of slave relative to
  31.                  * master. */
  32.     int width, height;        /* Desired dimensions of slave. */
  33.     struct MaintainSlave *nextPtr;
  34.                 /* Next in list of Maintains associated
  35.                  * with master. */
  36. } MaintainSlave;
  37.  
  38. /*
  39.  * For each window that has been specified as a master to
  40.  * Tk_MaintainGeometry, there is a structure of the following type:
  41.  */
  42.  
  43. typedef struct MaintainMaster {
  44.     Tk_Window ancestor;        /* The lowest ancestor of this window
  45.                  * for which we have *not* created a
  46.                  * StructureNotify handler.  May be the
  47.                  * same as the window itself. */
  48.     int checkScheduled;        /* Non-zero means that there is already a
  49.                  * call to MaintainCheckProc scheduled as
  50.                  * an idle handler. */
  51.     MaintainSlave *slavePtr;    /* First in list of all slaves associated
  52.                  * with this master. */
  53. } MaintainMaster;
  54.  
  55. /*
  56.  * Hash table that maps from a master's Tk_Window token to a list of
  57.  * Maintains for that master:
  58.  */
  59.  
  60. static Tcl_HashTable maintainHashTable;
  61.  
  62. /*
  63.  * Has maintainHashTable been initialized yet?
  64.  */
  65.  
  66. static int initialized = 0;
  67.  
  68. /*
  69.  * Prototypes for static procedures in this file:
  70.  */
  71.  
  72. static void        MaintainCheckProc _ANSI_ARGS_((ClientData clientData));
  73. static void        MaintainMasterProc _ANSI_ARGS_((ClientData clientData,
  74.                 XEvent *eventPtr));
  75. static void        MaintainSlaveProc _ANSI_ARGS_((ClientData clientData,
  76.                 XEvent *eventPtr));
  77.  
  78. /*
  79.  *--------------------------------------------------------------
  80.  *
  81.  * Tk_ManageGeometry --
  82.  *
  83.  *    Arrange for a particular procedure to manage the geometry
  84.  *    of a given slave window.
  85.  *
  86.  * Results:
  87.  *    None.
  88.  *
  89.  * Side effects:
  90.  *    Proc becomes the new geometry manager for tkwin, replacing
  91.  *    any previous geometry manager.  The geometry manager will
  92.  *    be notified (by calling procedures in *mgrPtr) when interesting
  93.  *    things happen in the future.  If there was an existing geometry
  94.  *    manager for tkwin different from the new one, it is notified
  95.  *    by calling its lostSlaveProc.
  96.  *
  97.  *--------------------------------------------------------------
  98.  */
  99.  
  100. void
  101. Tk_ManageGeometry(tkwin, mgrPtr, clientData)
  102.     Tk_Window tkwin;        /* Window whose geometry is to
  103.                  * be managed by proc.  */
  104.     Tk_GeomMgr *mgrPtr;        /* Static structure describing the
  105.                  * geometry manager.  This structure
  106.                  * must never go away. */
  107.     ClientData clientData;    /* Arbitrary one-word argument to
  108.                  * pass to geometry manager procedures. */
  109. {
  110.     register TkWindow *winPtr = (TkWindow *) tkwin;
  111.  
  112.     if ((winPtr->geomMgrPtr != NULL) && (mgrPtr != NULL)
  113.         && ((winPtr->geomMgrPtr != mgrPtr)
  114.         || (winPtr->geomData != clientData))
  115.         && (winPtr->geomMgrPtr->lostSlaveProc != NULL)) {
  116.     (*winPtr->geomMgrPtr->lostSlaveProc)(winPtr->geomData, tkwin);
  117.     }
  118.  
  119.     winPtr->geomMgrPtr = mgrPtr;
  120.     winPtr->geomData = clientData;
  121. }
  122.  
  123. /*
  124.  *--------------------------------------------------------------
  125.  *
  126.  * Tk_GeometryRequest --
  127.  *
  128.  *    This procedure is invoked by widget code to indicate
  129.  *    its preferences about the size of a window it manages.
  130.  *    In general, widget code should call this procedure
  131.  *    rather than Tk_ResizeWindow.
  132.  *
  133.  * Results:
  134.  *    None.
  135.  *
  136.  * Side effects:
  137.  *    The geometry manager for tkwin (if any) is invoked to
  138.  *    handle the request.  If possible, it will reconfigure
  139.  *    tkwin and/or other windows to satisfy the request.  The
  140.  *    caller gets no indication of success or failure, but it
  141.  *    will get X events if the window size was actually
  142.  *    changed.
  143.  *
  144.  *--------------------------------------------------------------
  145.  */
  146.  
  147. void
  148. Tk_GeometryRequest(tkwin, reqWidth, reqHeight)
  149.     Tk_Window tkwin;        /* Window that geometry information
  150.                  * pertains to. */
  151.     int reqWidth, reqHeight;    /* Minimum desired dimensions for
  152.                  * window, in pixels. */
  153. {
  154.     register TkWindow *winPtr = (TkWindow *) tkwin;
  155.  
  156.     /*
  157.      * X gets very upset if a window requests a width or height of
  158.      * zero, so rounds requested sizes up to at least 1.
  159.      */
  160.  
  161.     if (reqWidth <= 0) {
  162.     reqWidth = 1;
  163.     }
  164.     if (reqHeight <= 0) {
  165.     reqHeight = 1;
  166.     }
  167.     if ((reqWidth == winPtr->reqWidth) && (reqHeight == winPtr->reqHeight)) {
  168.     return;
  169.     }
  170.     winPtr->reqWidth = reqWidth;
  171.     winPtr->reqHeight = reqHeight;
  172.     if ((winPtr->geomMgrPtr != NULL)
  173.         && (winPtr->geomMgrPtr->requestProc != NULL)) {
  174.     (*winPtr->geomMgrPtr->requestProc)(winPtr->geomData, tkwin);
  175.     }
  176. }
  177.  
  178. /*
  179.  *----------------------------------------------------------------------
  180.  *
  181.  * Tk_SetInternalBorder --
  182.  *
  183.  *    Notify relevant geometry managers that a window has an internal
  184.  *    border of a given width and that child windows should not be
  185.  *    placed on that border.
  186.  *
  187.  * Results:
  188.  *    None.
  189.  *
  190.  * Side effects:
  191.  *    The border width is recorded for the window, and all geometry
  192.  *    managers of all children are notified so that can re-layout, if
  193.  *    necessary.
  194.  *
  195.  *----------------------------------------------------------------------
  196.  */
  197.  
  198. void
  199. Tk_SetInternalBorder(tkwin, width)
  200.     Tk_Window tkwin;        /* Window that will have internal border. */
  201.     int width;            /* Width of internal border, in pixels. */
  202. {
  203.     register TkWindow *winPtr = (TkWindow *) tkwin;
  204.  
  205.     if (width == winPtr->internalBorderWidth) {
  206.     return;
  207.     }
  208.     if (width < 0) {
  209.     width = 0;
  210.     }
  211.     winPtr->internalBorderWidth = width;
  212.  
  213.     /*
  214.      * All the slaves for which this is the master window must now be
  215.      * repositioned to take account of the new internal border width.
  216.      * To signal all the geometry managers to do this, just resize the
  217.      * window to its current size.  The ConfigureNotify event will
  218.      * cause geometry managers to recompute everything.
  219.      */
  220.  
  221.     Tk_ResizeWindow(tkwin, Tk_Width(tkwin), Tk_Height(tkwin));
  222. }
  223.  
  224. /*
  225.  *----------------------------------------------------------------------
  226.  *
  227.  * Tk_MaintainGeometry --
  228.  *
  229.  *    This procedure is invoked by geometry managers to handle slaves
  230.  *    whose master's are not their parents.  It translates the desired
  231.  *    geometry for the slave into the coordinate system of the parent
  232.  *    and respositions the slave if it isn't already at the right place.
  233.  *    Furthermore, it sets up event handlers so that if the master (or
  234.  *    any of its ancestors up to the slave's parent) is mapped, unmapped,
  235.  *    or moved, then the slave will be adjusted to match.
  236.  *
  237.  * Results:
  238.  *    None.
  239.  *
  240.  * Side effects:
  241.  *    Event handlers are created and state is allocated to keep track
  242.  *    of slave.  Note:  if slave was already managed for master by
  243.  *    Tk_MaintainGeometry, then the previous information is replaced
  244.  *    with the new information.  The caller must eventually call
  245.  *    Tk_UnmaintainGeometry to eliminate the correspondence (or, the
  246.  *    state is automatically freed when either window is destroyed).
  247.  *
  248.  *----------------------------------------------------------------------
  249.  */
  250.  
  251. void
  252. Tk_MaintainGeometry(slave, master, x, y, width, height)
  253.     Tk_Window slave;        /* Slave for geometry management. */
  254.     Tk_Window master;        /* Master for slave; must be a descendant
  255.                  * of slave's parent. */
  256.     int x, y;            /* Desired position of slave within master. */
  257.     int width, height;        /* Desired dimensions for slave. */
  258. {
  259.     Tcl_HashEntry *hPtr;
  260.     MaintainMaster *masterPtr;
  261.     register MaintainSlave *slavePtr;
  262.     int new, map;
  263.     Tk_Window ancestor, parent;
  264.  
  265.     if (!initialized) {
  266.     initialized = 1;
  267.     Tcl_InitHashTable(&maintainHashTable, TCL_ONE_WORD_KEYS);
  268.     }
  269.  
  270.     /*
  271.      * See if there is already a MaintainMaster structure for the master;
  272.      * if not, then create one.
  273.      */
  274.  
  275.     parent = Tk_Parent(slave);
  276.     hPtr = Tcl_CreateHashEntry(&maintainHashTable, (char *) master, &new);
  277.     if (!new) {
  278.     masterPtr = (MaintainMaster *) Tcl_GetHashValue(hPtr);
  279.     } else {
  280.     masterPtr = (MaintainMaster *) ckalloc(sizeof(MaintainMaster));
  281.     masterPtr->ancestor = master;
  282.     masterPtr->checkScheduled = 0;
  283.     masterPtr->slavePtr = NULL;
  284.     Tcl_SetHashValue(hPtr, masterPtr);
  285.     }
  286.  
  287.     /*
  288.      * Create a MaintainSlave structure for the slave if there isn't
  289.      * already one.
  290.      */
  291.  
  292.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  293.         slavePtr = slavePtr->nextPtr) {
  294.     if (slavePtr->slave == slave) {
  295.         goto gotSlave;
  296.     }
  297.     }
  298.     slavePtr = (MaintainSlave *) ckalloc(sizeof(MaintainSlave));
  299.     slavePtr->slave = slave;
  300.     slavePtr->master = master;
  301.     slavePtr->nextPtr = masterPtr->slavePtr;
  302.     masterPtr->slavePtr = slavePtr;
  303.     Tk_CreateEventHandler(slave, StructureNotifyMask, MaintainSlaveProc,
  304.         (ClientData) slavePtr);
  305.  
  306.     /*
  307.      * Make sure that there are event handlers registered for all
  308.      * the windows between master and slave's parent (including master
  309.      * but not slave's parent).  There may already be handlers for master
  310.      * and some of its ancestors (masterPtr->ancestor tells how many).
  311.      */
  312.  
  313.     for (ancestor = master; ancestor != parent;
  314.         ancestor = Tk_Parent(ancestor)) {
  315.     if (ancestor == masterPtr->ancestor) {
  316.         Tk_CreateEventHandler(ancestor, StructureNotifyMask,
  317.             MaintainMasterProc, (ClientData) masterPtr);
  318.         masterPtr->ancestor = Tk_Parent(ancestor);
  319.     }
  320.     }
  321.  
  322.     /*
  323.      * Fill in up-to-date information in the structure, then update the
  324.      * window if it's not currently in the right place or state.
  325.      */
  326.  
  327.     gotSlave:
  328.     slavePtr->x = x;
  329.     slavePtr->y = y;
  330.     slavePtr->width = width;
  331.     slavePtr->height = height;
  332.     map = 1;
  333.     for (ancestor = slavePtr->master; ; ancestor = Tk_Parent(ancestor)) {
  334.     if (!Tk_IsMapped(ancestor) && (ancestor != parent)) {
  335.         map = 0;
  336.     }
  337.     if (ancestor == parent) {
  338.         if ((x != Tk_X(slavePtr->slave))
  339.             || (y != Tk_Y(slavePtr->slave))
  340.             || (width != Tk_Width(slavePtr->slave))
  341.             || (height != Tk_Height(slavePtr->slave))) {
  342.         Tk_MoveResizeWindow(slavePtr->slave, x, y, width, height);
  343.         }
  344.         if (map) {
  345.         Tk_MapWindow(slavePtr->slave);
  346.         } else {
  347.         Tk_UnmapWindow(slavePtr->slave);
  348.         }
  349.         break;
  350.     }
  351.     x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width;
  352.     y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width;
  353.     }
  354. }
  355.  
  356. /*
  357.  *----------------------------------------------------------------------
  358.  *
  359.  * Tk_UnmaintainGeometry --
  360.  *
  361.  *    This procedure cancels a previous Tk_MaintainGeometry call,
  362.  *    so that the relationship between slave and master is no longer
  363.  *    maintained.
  364.  *
  365.  * Results:
  366.  *    None.
  367.  *
  368.  * Side effects:
  369.  *    The slave is unmapped and state is released, so that slave won't
  370.  *    track master any more.  If we weren't previously managing slave
  371.  *    relative to master, then this procedure has no effect.
  372.  *
  373.  *----------------------------------------------------------------------
  374.  */
  375.  
  376. void
  377. Tk_UnmaintainGeometry(slave, master)
  378.     Tk_Window slave;        /* Slave for geometry management. */
  379.     Tk_Window master;        /* Master for slave; must be a descendant
  380.                  * of slave's parent. */
  381. {
  382.     Tcl_HashEntry *hPtr;
  383.     MaintainMaster *masterPtr;
  384.     register MaintainSlave *slavePtr, *prevPtr;
  385.     Tk_Window ancestor;
  386.  
  387.     if (!initialized) {
  388.     initialized = 1;
  389.     Tcl_InitHashTable(&maintainHashTable, TCL_ONE_WORD_KEYS);
  390.     }
  391.  
  392.     if (!(((TkWindow *) slave)->flags & TK_ALREADY_DEAD)) {
  393.     Tk_UnmapWindow(slave);
  394.     }
  395.     hPtr = Tcl_FindHashEntry(&maintainHashTable, (char *) master);
  396.     if (hPtr == NULL) {
  397.     return;
  398.     }
  399.     masterPtr = (MaintainMaster *) Tcl_GetHashValue(hPtr);
  400.     slavePtr = masterPtr->slavePtr;
  401.     if (slavePtr->slave == slave) {
  402.     masterPtr->slavePtr = slavePtr->nextPtr;
  403.     } else {
  404.     for (prevPtr = slavePtr, slavePtr = slavePtr->nextPtr; ;
  405.         prevPtr = slavePtr, slavePtr = slavePtr->nextPtr) {
  406.         if (slavePtr == NULL) {
  407.         return;
  408.         }
  409.         if (slavePtr->slave == slave) {
  410.         prevPtr->nextPtr = slavePtr->nextPtr;
  411.         break;
  412.         }
  413.     }
  414.     }
  415.     Tk_DeleteEventHandler(slavePtr->slave, StructureNotifyMask,
  416.         MaintainSlaveProc, (ClientData) slavePtr);
  417.     ckfree((char *) slavePtr);
  418.     if (masterPtr->slavePtr == NULL) {
  419.     if (masterPtr->ancestor != NULL) {
  420.         for (ancestor = master; ; ancestor = Tk_Parent(ancestor)) {
  421.         Tk_DeleteEventHandler(ancestor, StructureNotifyMask,
  422.             MaintainMasterProc, (ClientData) masterPtr);
  423.         if (ancestor == masterPtr->ancestor) {
  424.             break;
  425.         }
  426.         }
  427.     }
  428.     if (masterPtr->checkScheduled) {
  429.         Tk_CancelIdleCall(MaintainCheckProc, (ClientData) masterPtr);
  430.     }
  431.     Tcl_DeleteHashEntry(hPtr);
  432.     ckfree((char *) masterPtr);
  433.     }
  434. }
  435.  
  436. /*
  437.  *----------------------------------------------------------------------
  438.  *
  439.  * MaintainMasterProc --
  440.  *
  441.  *    This procedure is invoked by the Tk event dispatcher in
  442.  *    response to StructureNotify events on the master or one
  443.  *    of its ancestors, on behalf of Tk_MaintainGeometry.
  444.  *
  445.  * Results:
  446.  *    None.
  447.  *
  448.  * Side effects:
  449.  *    It schedules a call to MaintainCheckProc, which will eventually
  450.  *    caused the postions and mapped states to be recalculated for all
  451.  *    the maintained slaves of the master.  Or, if the master window is
  452.  *    being deleted then state is cleaned up.
  453.  *
  454.  *----------------------------------------------------------------------
  455.  */
  456.  
  457. static void
  458. MaintainMasterProc(clientData, eventPtr)
  459.     ClientData clientData;        /* Pointer to MaintainMaster structure
  460.                      * for the master window. */
  461.     XEvent *eventPtr;            /* Describes what just happened. */
  462. {
  463.     MaintainMaster *masterPtr = (MaintainMaster *) clientData;
  464.     MaintainSlave *slavePtr;
  465.     int done;
  466.  
  467.     if ((eventPtr->type == ConfigureNotify)
  468.         || (eventPtr->type == MapNotify)
  469.         || (eventPtr->type == UnmapNotify)) {
  470.     if (!masterPtr->checkScheduled) {
  471.         masterPtr->checkScheduled = 1;
  472.         Tk_DoWhenIdle(MaintainCheckProc, (ClientData) masterPtr);
  473.     }
  474.     } else if (eventPtr->type == DestroyNotify) {
  475.     /*
  476.      * Delete all of the state associated with this master, but
  477.      * be careful not to use masterPtr after the last slave is
  478.      * deleted, since its memory will have been freed.
  479.      */
  480.  
  481.     done = 0;
  482.     do {
  483.         slavePtr = masterPtr->slavePtr;
  484.         if (slavePtr->nextPtr == NULL) {
  485.         done = 1;
  486.         }
  487.         Tk_UnmaintainGeometry(slavePtr->slave, slavePtr->master);
  488.     } while (!done);
  489.     }
  490. }
  491.  
  492. /*
  493.  *----------------------------------------------------------------------
  494.  *
  495.  * MaintainSlaveProc --
  496.  *
  497.  *    This procedure is invoked by the Tk event dispatcher in
  498.  *    response to StructureNotify events on a slave being managed
  499.  *    by Tk_MaintainGeometry.
  500.  *
  501.  * Results:
  502.  *    None.
  503.  *
  504.  * Side effects:
  505.  *    If the event is a DestroyNotify event then the Maintain state
  506.  *    and event handlers for this slave are deleted.
  507.  *
  508.  *----------------------------------------------------------------------
  509.  */
  510.  
  511. static void
  512. MaintainSlaveProc(clientData, eventPtr)
  513.     ClientData clientData;        /* Pointer to MaintainSlave structure
  514.                      * for master-slave pair. */
  515.     XEvent *eventPtr;            /* Describes what just happened. */
  516. {
  517.     MaintainSlave *slavePtr = (MaintainSlave *) clientData;
  518.  
  519.     if (eventPtr->type == DestroyNotify) {
  520.     Tk_UnmaintainGeometry(slavePtr->slave, slavePtr->master);
  521.     }
  522. }
  523.  
  524. /*
  525.  *----------------------------------------------------------------------
  526.  *
  527.  * MaintainCheckProc --
  528.  *
  529.  *    This procedure is invoked by the Tk event dispatcher as an
  530.  *    idle handler, when a master or one of its ancestors has been
  531.  *    reconfigured, mapped, or unmapped.  Its job is to scan all of
  532.  *    the slaves for the master and reposition them, map them, or
  533.  *    unmap them as needed to maintain their geometry relative to
  534.  *    the master.
  535.  *
  536.  * Results:
  537.  *    None.
  538.  *
  539.  * Side effects:
  540.  *    Slaves can get repositioned, mapped, or unmapped.
  541.  *
  542.  *----------------------------------------------------------------------
  543.  */
  544.  
  545. static void
  546. MaintainCheckProc(clientData)
  547.     ClientData clientData;        /* Pointer to MaintainMaster structure
  548.                      * for the master window. */
  549. {
  550.     MaintainMaster *masterPtr = (MaintainMaster *) clientData;
  551.     MaintainSlave *slavePtr;
  552.     Tk_Window ancestor, parent;
  553.     int x, y, map;
  554.  
  555.     masterPtr->checkScheduled = 0;
  556.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  557.         slavePtr = slavePtr->nextPtr) {
  558.     parent = Tk_Parent(slavePtr->slave);
  559.     x = slavePtr->x;
  560.     y = slavePtr->y;
  561.     map = 1;
  562.     for (ancestor = slavePtr->master; ; ancestor = Tk_Parent(ancestor)) {
  563.         if (!Tk_IsMapped(ancestor) && (ancestor != parent)) {
  564.         map = 0;
  565.         }
  566.         if (ancestor == parent) {
  567.         if ((x != Tk_X(slavePtr->slave))
  568.             || (y != Tk_Y(slavePtr->slave))) {
  569.             Tk_MoveWindow(slavePtr->slave, x, y);
  570.         }
  571.         if (map) {
  572.             Tk_MapWindow(slavePtr->slave);
  573.         } else {
  574.             Tk_UnmapWindow(slavePtr->slave);
  575.         }
  576.         break;
  577.         }
  578.         x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width;
  579.         y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width;
  580.     }
  581.     }
  582. }
  583.